import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Request } from "express";

import { CONFIG_KEY } from "@/core/config/configurations";
import { PrismaService } from "@/core/db/prisma.service";
import BusinessException from "@/core/exception/BusinessException";
import { AvatarUtils } from "@/core/utils/avatar.utils";
import { BcryptUtils } from "@/core/utils/BcryptUtils";
import { PathUtils } from "@/core/utils/path.utils";
import { RequestUrlUtil } from "@/core/utils/request-url.utils";
import { PageResultVO } from "@/core/vo/page-result.vo";

import { CreateSysUserDto } from "./dto/create-sys-user.dto";
import { QuerySysUserDto } from "./dto/query-sys-user.dto";
import { UpdateSysUserDto } from "./dto/update-sys-user.dto";
import { SysUserVo } from "./vo/sys-user.vo";

@Injectable()
export class SysUserService {
    constructor(
        private readonly prismaService: PrismaService,
        private readonly configService: ConfigService
    ) {}

    /**
     * 创建用户
     * @param createSysUserDto
     */
    async create(createSysUserDto: CreateSysUserDto, req: Request) {
        const { username, email, password, nickname, avatar } = createSysUserDto;
        const user = await this.prismaService.sysUser.findFirst({
            where: {
                OR: [{ username: username }, { email: email }],
                AND: {
                    isDeleted: false
                }
            }
        });
        if (user) throw new BusinessException("USER_EXIST");

        const hashedPwd = await BcryptUtils.hashPassword(password);
        const newUser = await this.prismaService.sysUser.create({
            data: {
                ...createSysUserDto,
                password: hashedPwd,
                nickname: nickname || "用户" + username,
                avatar: avatar || AvatarUtils.getRandomUrl()
            }
        });
        if (newUser.avatar && !newUser.avatar.startsWith("http")) {
            const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
            const baseUrl = RequestUrlUtil.getBaseUrl(req);
            newUser.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, newUser.avatar);
        }
        return new SysUserVo(newUser);
    }

    /**
     * 查询用户列表
     * @param querySysUserDto
     */
    async findListPage(querySysUserDto: QuerySysUserDto, req: Request) {
        const {
            current,
            size,
            createdAtStart,
            createdAtEnd,
            updatedAtStart,
            updatedAtEnd,
            username,
            nickname,
            isDisabled,
            isMultipoint
        } = querySysUserDto;

        const queryConditions = {
            AND: [
                {
                    isDeleted: false
                },
                {
                    OR: [
                        {
                            username: {
                                contains: username
                            }
                        },
                        {
                            nickname: {
                                contains: nickname
                            }
                        }
                    ]
                },
                {
                    createdAt: {
                        gte: createdAtStart,
                        lte: createdAtEnd
                    }
                },
                {
                    updatedAt: {
                        gte: updatedAtStart,
                        lte: updatedAtEnd
                    }
                },
                {
                    isDisabled: {
                        equals: isDisabled
                    }
                },
                {
                    isMultipoint: {
                        equals: isMultipoint
                    }
                }
            ]
        };
        // 并行查询
        const [listData, total] = await Promise.all([
            this.prismaService.sysUser.findMany({
                skip: (current - 1) * size,
                take: size,
                where: queryConditions,
                orderBy: {
                    createdAt: "desc" // 按创建时间降序。降序：从大到小。时间就是从晚到早。
                }
            }),
            this.prismaService.sysUser.count({
                where: queryConditions
            })
        ]);
        const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
        const baseUrl = RequestUrlUtil.getBaseUrl(req);
        const listUserVo = listData.map(user => {
            if (user.avatar && !user.avatar.startsWith("http")) {
                user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, user.avatar);
            }
            return new SysUserVo(user);
        });

        const pages = Math.ceil(total / size);

        return new PageResultVO(listUserVo, total, pages, current, size);
    }

    /**
     * 查询所有用户列表
     */
    async findAll(req: Request) {
        const listData = await this.prismaService.sysUser.findMany({
            where: {
                isDeleted: false
            },
            orderBy: {
                createdAt: "desc" // 按创建时间降序。降序：从大到小。时间就是从晚到早。
            }
        });
        const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
        const baseUrl = RequestUrlUtil.getBaseUrl(req);
        const listUserVo = listData.map(user => {
            if (user.avatar && !user.avatar.startsWith("http")) {
                user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, user.avatar);
            }
            return new SysUserVo(user);
        });
        return listUserVo;
    }

    /**
     * 查询用户详情，包括部门、角色、岗位详细信息
     * @param id
     */
    async findOne(id: number, req: Request) {
        const sysUser = await this.prismaService.sysUser.findFirst({
            where: {
                id: id,
                isDeleted: false
            }
        });
        if (sysUser.avatar && !sysUser.avatar.startsWith("http")) {
            const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
            const baseUrl = RequestUrlUtil.getBaseUrl(req);
            sysUser.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, sysUser.avatar);
        }
        return new SysUserVo(sysUser);
    }

    /**
     * 设置超级管理员
     * @param targetUserId
     * @returns
     */
    async setSuperAdmin(targetUserId: number, req: Request) {
        let user;
        try {
            user = await this.prismaService.sysUser.update({
                where: {
                    id: targetUserId
                },
                data: {
                    isSuper: true
                }
            });
        } catch (error) {
            throw new BusinessException();
        }
        if (user.avatar && !user.avatar.startsWith("http")) {
            const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
            const baseUrl = RequestUrlUtil.getBaseUrl(req);
            user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, user.avatar);
        }
        return new SysUserVo(user);
    }

    /**
     * 更新用户信息
     * @param id
     * @param updateSysUserDto
     */
    async update(id: number, updateSysUserDto: UpdateSysUserDto, req: Request) {
        const { ...rest } = updateSysUserDto;
        let user;
        try {
            user = await this.prismaService.sysUser.update({
                where: {
                    id,
                    isDeleted: false
                },
                data: {
                    ...rest
                }
            });
        } catch (error) {
            throw new BusinessException("USER_NOT_FOUND");
        }
        if (user.avatar && !user.avatar.startsWith("http")) {
            const { appServerUrlPrefix } = this.configService.get(CONFIG_KEY.server);
            const baseUrl = RequestUrlUtil.getBaseUrl(req);
            user.avatar = PathUtils.joinUrl(baseUrl, appServerUrlPrefix, user.avatar);
        }
        return new SysUserVo(user);
    }

    /**
     * 更新用户密码
     * @param id
     * @param oldPassword
     * @param newPassword
     */
    async updatePassword(id: number, oldPassword: string, newPassword: string) {
        const user = await this.prismaService.sysUser.findUnique({
            where: {
                id,
                isDeleted: false
            },
            select: {
                password: true
            }
        });

        if (!user) throw new BusinessException("USER_NOT_FOUND");

        const isMatch = await BcryptUtils.comparePassword(oldPassword, user.password);
        if (!isMatch) throw new BusinessException("OLD_PASSWORD_ERROR");

        try {
            await this.prismaService.sysUser.update({
                where: {
                    id,
                    isDeleted: false
                },
                data: {
                    password: await BcryptUtils.hashPassword(newPassword)
                }
            });
        } catch (error) {
            throw new BusinessException();
        }
    }

    /**
     * 删除用户
     * @param id
     * @param softDelete
     */
    async remove(id: number, softDelete: boolean = true) {
        try {
            if (softDelete) {
                await this.prismaService.sysUser.update({
                    where: {
                        id,
                        isDeleted: false
                    },
                    data: {
                        isDeleted: true,
                        deletedAt: new Date()
                    }
                });
            } else {
                await this.prismaService.sysUser.delete({
                    where: {
                        id,
                        isDeleted: false
                    }
                });
            }
        } catch (error) {
            throw new BusinessException();
        }
    }

    /**
     * 批量删除用户
     * @param ids
     * @param softDelete
     */
    async removeBatch(ids: number[], softDelete: boolean = true) {
        let count;
        if (softDelete) {
            count = await this.prismaService.sysUser.updateMany({
                where: {
                    id: {
                        in: ids
                    },
                    isDeleted: false
                },
                data: {
                    isDeleted: true,
                    deletedAt: new Date()
                }
            });
        } else {
            count = await this.prismaService.sysUser.deleteMany({
                where: {
                    id: {
                        in: ids
                    },
                    isDeleted: false
                }
            });
        }
        return count;
    }
}
